package com.huangfuyi.process.actuator.threadpool;

import com.huangfuyi.process.actuator.definition.EPBeforeRunning;
import com.huangfuyi.process.actuator.definition.EPConditionParser;
import com.huangfuyi.process.actuator.definition.EPNodeApp;
import com.huangfuyi.process.actuator.enums.EPInstanceStatus;
import com.huangfuyi.process.actuator.enums.EPNodeStatus;
import com.huangfuyi.process.actuator.enums.EPNodeType;
import com.huangfuyi.process.actuator.enums.EPParamType;
import com.huangfuyi.process.entity.instance.EPInstanceEntity;
import com.huangfuyi.process.entity.mainthread.EPInstance;
import com.huangfuyi.process.entity.mainthread.EPSchedulerInfo;
import com.huangfuyi.process.entity.model.EPModelEntity;
import com.huangfuyi.process.entity.node.EPNodeEntity;
import com.huangfuyi.process.entity.node.EPNodeNextConfigEntity;
import com.huangfuyi.process.entity.running.EPParamEntity;
import com.huangfuyi.process.entity.running.EPRunningEntity;
import com.huangfuyi.process.exceptions.EPExecutorException;
import com.huangfuyi.process.managers.EPNodeAppManager;
import com.huangfuyi.process.managers.EPBeforeRunningManager;
import com.huangfuyi.process.managers.EPManagers;
import com.huangfuyi.process.service.EPCoreInstanceService;
import com.huangfuyi.process.service.EPCoreModelService;
import com.huangfuyi.process.service.EPCoreRunningService;
import com.huangfuyi.process.utils.EPIdUtils;
import com.huangfuyi.process.utils.EPObjectUtils;
import com.huangfuyi.process.utils.EPParamUtils;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

public class EPMainThread extends Thread {

    private static final Logger log = Logger.getLogger("MainThread");

    // 当前执行的任务信息
    private EPInstance instance;
    //标识是否为慢线程
    private volatile boolean isSlow = false;
    // 当前任务执行起始时间
    private volatile long startTime = -1L;
    //是否对单个流程实例限制最大运行节点数最
    private boolean whetherLimitNodesCount = true;
    // 对单个流程进行最大运行*
    private int nodesCountLimit = 1000;

    private final EPCoreRunningService<? extends EPRunningEntity, ? extends EPParamEntity> runningService;

    private final EPNodeAppManager nodeAppManager;

    private final EPCoreInstanceService<? extends EPInstanceEntity, ? extends EPModelEntity, ? extends EPNodeEntity, ? extends EPNodeNextConfigEntity, ? extends EPRunningEntity, ? extends EPParamEntity> instanceService;

    private final EPCoreModelService<? extends EPModelEntity, ? extends EPNodeEntity, ? extends EPNodeNextConfigEntity> modelService;

    private final EPConditionParser conditionParser;

    @Override
    public void run() {
        while (!isSlow) {
            try {
                //获取任务信息
                this.instance = EPMainThreadPool.taskQueue.poll(1L, TimeUnit.MINUTES);
                if (this.instance == null) {
                    continue;
                }
            } catch (Throwable e) {
                log.warning(String.format(Locale.ROOT, "主线程: 获取待执行任务信息失败！原因：%s", e.getMessage()));
                continue;
            }
            try {
                log.config(String.format(Locale.ROOT, "主线程: 获取到待运行任务信息，实例id：%s； 节点id：%s", instance.getId(), instance.getCurrentNodeId()));
                //更新任务开始时间，用于识别设任务
                this.startTime = System.currentTimeMillis();

                // 运行节点
                runTaskNode(instance);
                log.config(String.format(Locale.ROOT, "主线程（实例id：%s； 节点id：%s）: 节点运行成功，", instance.getId(), instance.getCurrentNodeId()));
            } catch (Throwable t) {
                StringWriter stackInfo = new StringWriter();
                t.printStackTrace(new PrintWriter(stackInfo));
                String errorMessage = stackInfo.toString();
                log.config(String.format(Locale.ROOT, "主线程（实例id：%s； 节点id：%s）: 节点运行失败，原因：%s", instance.getId(), instance.getCurrentNodeId(), errorMessage));
                runningService.updateAndAppendMessage(instance.getCurrentRunningId(), EPNodeStatus.FAILED, errorMessage);
            } finally {
                // 无论发生什么异常，都初始化下一节点
                next(instance);
                this.startTime = -1L;
            }
        }
    }


    private void runTaskNode(EPInstance instance) {
        log.config(String.format(Locale.ROOT, "主线程（实例id：%s； 节点id：%s）：初始化节点运行信息.....", instance.getId(), instance.getCurrentNodeId()));
        runningService.updateAndAppendMessage(instance.getCurrentRunningId(), EPNodeStatus.RUNNING, "");

        // 获取节点要执行的方法
        EPNodeEntity currentNode = modelService.queryNodeById(instance.getModelId(), instance.getCurrentNodeId());
        String appConfig = currentNode.getApplication();

        try {
            // 开始和结束节点不执行任何方法
            if (!EPNodeType.START.equals(currentNode.getNodeType()) && !EPNodeType.END.equals(currentNode.getNodeType())) {
                if (EPObjectUtils.isEmpty(appConfig)) {
                    throw new EPExecutorException("节点业务配置不能为空！");
                }
                EPNodeApp app = nodeAppManager.getApp(appConfig);
                if (EPObjectUtils.isEmpty(app)) {
                    throw new EPExecutorException(String.format(Locale.ROOT, "无法通过业务配置获取到对应实现，配置：%s", app));
                }
                app.run(instance);
            }
        } finally {
            // 无论发生什么异常，都要保存运行参数
            runningService.saveParams(this.instance.getId(), this.instance.getCurrentRunningId(), EPParamType.OUT, instance.getParams());
        }
        runningService.updateAndAppendMessage(instance.getCurrentRunningId(), EPNodeStatus.SUCCESS, instance.getMessage());
    }


    private void next(EPInstance instance) {
        try {
            int finishedNodeCount = runningService.countByInstanceId(instance.getId());
            if (this.whetherLimitNodesCount && finishedNodeCount >= this.nodesCountLimit) {
                throw new EPExecutorException(String.format(Locale.ROOT, "当前流程实例运行节点数量：%d，已达到单个实例节点运行数量上限", finishedNodeCount));
            }

            // 流程实例对象中的节点信息为刚运行完的节点
            EPNodeEntity finishedNode = modelService.queryNodeById(instance.getModelId(), instance.getCurrentNodeId());

            // 若节点类型为结束节点，说明流程结束
            if (EPNodeType.END.equals(finishedNode.getNodeType())) {
                instanceService.updateStatusAndMsg(instance.getId(), EPInstanceStatus.FINISHED, "");
                return;
            }

            // 查询实例运行状态，流程实例可能被暂停或终止，这里对运行状态做检查
            EPInstanceEntity currentInstance = instanceService.queryById(instance.getId());
            if (EPObjectUtils.isEmpty(currentInstance)) {
                throw new EPExecutorException(String.format(Locale.ROOT, "无法查询到流程实例的运行信息，实例id：%s", instance.getId()));
            }

            EPInstanceStatus currentInstanceStatus = currentInstance.getStatus();
            if (EPInstanceStatus.FAILED.equals(currentInstanceStatus) || EPInstanceStatus.FINISHED.equals(currentInstanceStatus) || EPInstanceStatus.STOP.equals(currentInstanceStatus)) {
                return;
            }

            // 根据已完成节点的运行结果，选择流程后续走向
            EPRunningEntity finishedRunningInfo = runningService.queryById(instance.getCurrentRunningId());
            if (EPObjectUtils.isEmpty(finishedRunningInfo)) {
                throw new EPExecutorException(String.format(Locale.ROOT, "无法查询到流程节点的运行结果，运行时id：%s", instance.getCurrentRunningId()));
            }

            EPNodeStatus finishedRunningInfoStatus = finishedRunningInfo.getStatus();
            String nextNodeId = null;
            if (EPNodeStatus.SUCCESS.equals(finishedRunningInfoStatus)) {
                // 上一节点运行成功，从nextConfig中获取后续节点
                List<? extends EPNodeNextConfigEntity> nextConfig = modelService.queryNextConfigByNodeId(finishedNode.getModelId(), finishedNode.getId());

                // 判断并获取符合条件的后续节点id
                boolean got = false;
                for (EPNodeNextConfigEntity nextNode : nextConfig) {
                    boolean checkResult = conditionParser.doCheck(nextNode.getCondition(), instance.getParams());

                    if (checkResult) {
                        got = true;
                        nextNodeId = nextNode.getPointNodeId();
                        break;
                    }
                }

                if (!got) {
                    throw new EPExecutorException("没有符合条件的后续节点配置。");
                }
            } else {
                String errorHandelNodeId = finishedNode.getErrorHandelNodeId();
                if (EPObjectUtils.isEmpty(errorHandelNodeId)) {
                    throw new EPExecutorException("节点业务运行异常且没有配置异常处理节点，流程结束。");
                } else {
                    nextNodeId = errorHandelNodeId;
                }
            }
            createTask(instance, currentInstanceStatus, nextNodeId);

        } catch (Throwable t) {
            StringWriter stackInfo = new StringWriter();
            t.printStackTrace(new PrintWriter(stackInfo));
            String errorMessage = stackInfo.toString();
            instanceService.updateStatusAndMsg(instance.getId(), EPInstanceStatus.FAILED, errorMessage);
        }
    }

    // 创建任务
    private void createTask(EPInstance instance, EPInstanceStatus currentInstanceStatus, String nextNodeId) {
        EPNodeEntity readyNode = modelService.queryNodeById(instance.getModelId(), nextNodeId);
        if(EPObjectUtils.isEmpty(readyNode)){
            throw new EPExecutorException(String.format(Locale.ROOT, "无法查询到后续节点配置信息，节点id：%s", nextNodeId));
        }

        // 构建节点运行时信息
        EPRunningEntity readyRunning = new EPRunningEntity(EPIdUtils.getId(), instance.getId(), readyNode.getId(), EPNodeStatus.READY, "");

        // 构建实例运行信息，为前置方法提供数据
        EPInstance readyInstance = new EPInstance(instance, currentInstanceStatus, readyNode.getId(), readyRunning.getId(), instance.getParams());

        // 执行节点的前置处理方法
        String beforeRunningConfig = readyNode.getBeforeRunningConfig();
        if(!EPObjectUtils.isEmpty(beforeRunningConfig)){
            EPBeforeRunningManager beforeRunningManager = EPManagers.getBeforeRunningManager();
            if(!EPObjectUtils.isEmpty(beforeRunningManager)){
                EPBeforeRunning beforeRunning = beforeRunningManager.getBeforeRunning(beforeRunningConfig);
                if(EPObjectUtils.isEmpty(beforeRunning)){
                    log.warning(String.format(Locale.ROOT, "无法获取到前置处理逻辑实现，前置处理配置：%s", beforeRunningConfig));
                }else{
                    beforeRunning.doRun(readyInstance, beforeRunningConfig);
                }
            }else{
                log.warning("前置处理逻辑管理器未实现！");
            }
        }

        // 保存运行参数
        runningService.saveParams(readyInstance.getId(), readyRunning.getId(), EPParamType.IN, readyInstance.getParams());

        // 保存节点运行时信息
        runningService.add(readyRunning);

        // 若流程实例为暂停状态，则不将节点提交到执行器
        if(EPInstanceStatus.SUSPENDING.equals(currentInstanceStatus) || EPInstanceStatus.SUSPEND.equals(currentInstanceStatus) ){
            runningService.updateAndAppendMessage(readyRunning.getId(), EPNodeStatus.SUSPEND, "instance suspend.");
            return;
        }

        // 结束节点、自动节点立即执行；阻断节点存在调度配置时，提交到调度
        if(EPNodeType.TASK_AUTO.equals(readyNode.getNodeType()) || EPNodeType.END.equals(readyNode.getNodeType())){
            EPSchedulerInfo schedulerInfo = new EPSchedulerInfo();
            schedulerInfo.setRunningId(readyRunning.getId());
            schedulerInfo.setInstanceId(readyInstance.getId());
            schedulerInfo.setSchedulerConfig(readyNode.getSchedulerConfig());
            EPManagers.getScheduler().runNow(schedulerInfo);
        }else if(EPNodeType.TASK_MAN.equals(readyNode.getNodeType())){
            // 阻断节点初始化后，将状态修改为suspend
            runningService.updateAndAppendMessage(readyRunning.getId(), EPNodeStatus.SUSPEND, "");
            String schedulerConfig = String.valueOf(EPParamUtils.getValueDefaultKey(readyInstance.getParams(), readyNode.getSchedulerConfig()));
            if(!EPObjectUtils.isEmpty(schedulerConfig)){
                EPSchedulerInfo schedulerInfo = new EPSchedulerInfo();
                schedulerInfo.setRunningId(readyRunning.getId());
                schedulerInfo.setInstanceId(readyInstance.getId());
                schedulerInfo.setSchedulerConfig(schedulerConfig);
                EPManagers.getScheduler().submit(schedulerInfo);
            }
        }else{
            throw new EPExecutorException(String.format(Locale.ROOT, "不支持的节点类型：%s", readyNode.getNodeType()));
        }

    }

    public long getStartTime() {
        return this.startTime;
    }

    // 将当前线程标记为慢任务线程
    public void markAsSlow() {
        this.isSlow = true;
    }

    // 构造方法保护，不允许使用者创建
    protected EPMainThread(EPCoreRunningService<? extends EPRunningEntity, ? extends
            EPParamEntity> runningService, EPNodeAppManager nodeAppManager, EPCoreInstanceService<? extends
            EPInstanceEntity, ? extends EPModelEntity, ? extends EPNodeEntity, ? extends EPNodeNextConfigEntity, ? extends
            EPRunningEntity, ? extends EPParamEntity> instanceService, EPCoreModelService<? extends EPModelEntity, ? extends
            EPNodeEntity, ? extends EPNodeNextConfigEntity> modelService, EPConditionParser conditionParser) {
        this.runningService = runningService;
        this.nodeAppManager = nodeAppManager;
        this.instanceService = instanceService;
        this.modelService = modelService;
        this.conditionParser = conditionParser;
    }

    public EPInstance getInstance() {
        return instance;
    }
}
